KVS intro Итак, допустим, мы имеем индуктивные биндинг к Mnesia. Теперь нам нужно реализовать публичный API KVS на EXE. API поделено на три группы. Все функции не зависят от какого либо стейта кроме самого драйвера. Все драйвера реализауют первый уровень и часть второго. Третий уровень шугар над драйверами, обеспечивающий версионирование. Про него и пост этот. -export([start/0,stop/0]). % service -export([destroy/0,join/0,join/1,init/2]). % schema change -export([modules/0,containers/0,tables/0,table/1,version/0]). % meta info -export([create/1,add/1,link/1,remove/2]). % chain ops -export([put/1,delete/2,next_id/2]). % raw ops -export([get/2,get/3,index/3]). % read ops -export([rotate/1,limit/1,forget/1]). % versioning -export([last_table/1,last_disc/1,rname/1,nname/1,]). 1) группа сервиса базы данных старт/стоп, создание реплики, синхронизация между репликами, удаление реплики, получения таблицы или всей директории с контейнерами. > kvs:join(). ok > kvs:dir(). [{table,access}, {table,acl}, {table,subscription}, {table,user}, {table,group}, {table,feed}, {table,comment}, {table,entry}, {table,id_seq}, {table,operation}, {table,log}, {table,config}, {table,transaction}] > kvs:all(id_seq). [{id_seq,"access.tables",1}, {id_seq,"acl.tables",1}, {id_seq,"log.tables",1}, {id_seq,"user.tables",1}, {id_seq,"card.tables",1}, {id_seq,"feed.tables",1}, {id_seq,"process.tables",1}, {id_seq,"entry.tables",1}, {id_seq,"id_seq.tables",1}, {id_seq,"operation.tables",1}] > kvs:containers(). [{log,[id,top,count,name,acc]}, {entry,[id,version,container,feed_id,prev,next,feeds,guard, etc,entry_id,from,to,title,description,created,hidden, access,shared,starred,deleted,media,type]}, {comment,[id,version,container,feed_id,prev,next,feeds, guard,etc,comment_id,entry_id,content,from,created,media, parent]}, {feed,[id,top,count,aclver]}, {acl,[id,top,count]}] 2) группа создания и удаления связей. Причем create/1, add/1, link/1, remove/2 — полиморфные функции работающие с любыми драйверами. Остальные get, put, delete, next_id — редиреректы на драйвера. 3) Чтобы показать, что такое KVS я сразу перейду к уровню хранения таблиц и версионированию. Таблицы хранят диапазоны индексов, диапазоны всегда последовательны, не пересекаются, нет дырок, таблицы хранятся всегда в файлах. каждый файл — это реальная таблицы mnesia, поэтому количество элементов виртуальной последовательности элементов таблицы — бесконечен, так как эрланг реализует GMP целочисленную библиотеку. Чтобы показать как происходит авторотейт таблиц зафиксируем границу ротейта в 6 записей и добавим в стрим users 30 записей: > [ kvs:add(#user{id=kvs:next_id("user",1)}) || _ <- lists:seq(1,30) ], ok. ok Посмотрим что получилось: > kvs:id_seq(user). 30 > kvs:config(user). [{interval,31,infinity,user6,user}, {interval,25,30,user5,user}, {interval,19,24,user4,user}, {interval,13,18,user3,user}, {interval,7,12,user2,user}] Тут видно по предпоследнему полю, что стрим #user{} размещен в 6 файлах (диапазон #user1:[1—6] не показывается). В первой и второй колонке — граничные id диапазона конкретной таблицы. Последнее поле хранит имя текущего бизнес-рекорда: если скажем версия рекорда поменяется на #user2{} то он последнее поле рекорда #interval будет не user а user2, таким образом реализуется версионирование рекордов, про которое я писал недавно, что его реализация для KVS заняла 10 строчек. Убедиться, что рекорды сохраняют линковку (пары чисел вунтри рекордов это поля #user.prev и #user.next) можно вызвав рекурсор таблицы или вызвав length у всей последовательности: > mnesia:transaction(fun() -> qlc:eval(mnesia:table(user4)) end). {atomic,[{user4,19,[],feed,user,18,20,[],true, [],[],[],[],[],[], [],[],[],[],[],[], [],[],[],[]}, {user4,20,[],feed,user,19,21,[],true,[], [],[],[],[],[],[], [],[],[],[],[],[], [],[],[]}, {user4,21,[],feed,user,20,22,[],true,[], [],[],[],[],[],[], [],[],[],[],[],[], [],[],...}, {user4,22,[],feed,user,21,23,[],true,[], [],[],[],[],[],[], [],[],[],[],[],[], [],...}, {user4,23,[],feed,user,22,24,[],true,[], [],[],[],[],[],[], [],[],[],[],[],[],...}, {user4,24,[],feed,user,23,25,[],true,[], [],[],[],[],[],[], [],[],[],[],[],...}]} > length(kvs:entries(kvs:get(feed,user),user,infinity)). 30 Траверсал при этом сохраняется, т.е. фактически стрим может содержать объекты разных типов (и даже не обязательно user), но в типизированной версии KVS на EXE я думаю можно оганичиться одним типом, просто с версиями и с точностью до ротейтов таблиц (там все целочиселнное, так что Nat хватит). Напоминаю, если это вам кажется слишком сложным, что код всея KVS занимает 300 строк, конечно его типизированная версия будет больше (только за счет аннотаций), но не думаю, что намного. Вот такая петрушка этот KVS. Вместо мнезии я хочу взять какое-то LSM Tree, кандидат — hanoidb (чтобы в Xen работало), и взлетать. Но если упороться и использовать HN0 C++ бекенд, то можно генерировать код, который упаковывает оптимально EXE рекорды в сектора на диске. Ну и там еще маленький HAL слой для сшивки с драйверами дискового контроллера. Это программа максимум конечно, хотя тут конечно придется выбирать как и с чем линковаться: использовать LING или писать свою виртуальную машину на EXE/C++, но знаю точно, что если чего-то очень захотеть (например написать собственный прувер для своей компании), то это можно получить. Вообще-то если мы захотим использовать KVS не как CoSQL сторадж а как SQL сторадж (а это значит что вместо ссылок на дочерние элементы мы храним ссылки на родительские, перевернутые стрелки), то нижележащий слой версионирования не изменится! Все будет так же мапредюсится по таблицам, все фолды и джойны, которые мы руками пишем на KVS (да, мы бедные и у нас нет высоэффективных компиляторов кверей пока). Вообщем эта схема, которую я нащупал и испробовал мне показалась очень эффективной. Если кто-то видит что я хуйню тут гоню, можете сказать — но только по делу! Главная идея — компактно пакуем рекорды в C-структуры, режем на таблицы и используем последовательности в качестве индексов. Никаких новостей — все именно так, как и происходит во всех базах. Добавляем сюда полиморфные рекорды с базой в виде бинарного дерева: id, prev, next (но можно и расширять), делаем правые (next) и левые (prev) фолды для обхода деревьев (все как в MUMPS этой вашей с Глобалами, да и мнезия внутри так работает). И погнали! — хоть SQL — хоть Blockchain — хоть репозиторий банковских транзакций — хоть графовую базу — хоть котиков в фид-скролах — хоть персистанс корекурсивных инфинити-групоидов на KVS пишите и все это там храните! KVS — Framework for building Databases. Автора: DOXTOP, MAXIM, AKALENUK и другие коммитеры synrc/kvs (всего восемь). Последний релиз — версия 3.3 (3 года непрерывного продакшина). Примеры реализованых распределенных транзакционных баз данных на KVS: ___________________ [1]. Chain Replication Database: http://synrc.com/apps/cr/doc/cr.htm (прототип)